//
//  TSCollectionViewComponent.swift
//  ClockWidget
//
//  Created by TSYH on 2023/7/18.
//

import UIKit



public let kIndexPath = "kIndexPath"

open class TSCollectionView: UICollectionView {
    lazy var reuseSubviews: [String: UIView] = [:]
    
    func cacheReuseView(_ view: UIView, reuseId: String?) {
        guard let reuseId = reuseId, !reuseId.isEmpty else {
            return
        }
        reuseSubviews[reuseId] = view
    }
    
    func dequeueReuseSubviews(for reuseId: String?) -> UIView? {
        guard let reuseId = reuseId, !reuseId.isEmpty else {
            return nil
        }
        return reuseSubviews[reuseId]
    }
    
    func clearReuseSubviews() {
        reuseSubviews.removeAll()
    }
    
    public var isCanGestureRecognizer:Bool = true
    open override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
        return isCanGestureRecognizer
    }
}

open class TSCollectionViewComponent: NSObject {
    
    open var sectionActionHandler: ((Any?, IndexPath) -> Void)?

    open var itemDidSelectedHandler: ((Any?, IndexPath) -> Void)?
    open var itemActionHandler: ((Any?, IndexPath) -> Void)?
    
    open var itemWillDisplayHandler: ((UICollectionViewCell, Any?, IndexPath) -> Void)?
    open var itemDidEndDisplayingHandler: ((UICollectionViewCell, Any?, IndexPath) -> Void)?
    
    open var didScrollHandler: ((UIScrollView) -> Void)?
    open var willBeginDraggingHandler: ((UIScrollView) -> Void)?
    open var didEndDraggingHandler: ((UIScrollView, Bool) -> Void)?
    open var didScrollToTopHandler: ((UIScrollView) -> Void)?
    open var willEndDraggingWithVelocityHandler: ((UIScrollView, CGPoint) -> Void)?
    open var didEndDeceleratingHandler: ((UIScrollView) -> Void)?
    
    open var collectionView: TSCollectionView!
    open var attributes: [String: Any]?
    
    
    
    
    
    public static let defaultCellID: String = "Component_Default_Cell_ID"
    public init(frame: CGRect, layout: UICollectionViewFlowLayout? = nil, attributes: [String: Any]? = nil) {
        super.init()
        
        var clLayout = UICollectionViewFlowLayout()
        if let direction = attributes?["direction"] as? UICollectionView.ScrollDirection {
            clLayout.scrollDirection = direction
        }
        
        if let layout = layout {
            clLayout = layout
        }
        collectionView = TSCollectionView(frame: frame, collectionViewLayout: clLayout)
        collectionView.delegate = self
        collectionView.dataSource = self
        collectionView.contentInsetAdjustmentBehavior = .never
        collectionView.showsVerticalScrollIndicator = false
        collectionView.showsHorizontalScrollIndicator = false
        collectionView.backgroundColor = .clear
        
        if #available(iOS 11.0, *) {
            collectionView.contentInsetAdjustmentBehavior = .never// 关闭自动调整
        }
        
        self.attributes = attributes
        if let showScrollIndicator = attributes?["showScrollIndicator"] as? Bool {
            collectionView.showsHorizontalScrollIndicator = showScrollIndicator
        }
        
        collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: Self.defaultCellID)
    }
    
    open lazy var dataSource = [TSComponent]()
    public func clear() {
        dataSource = []
    }
    
    public func reloadView(with components: [TSComponent]) {
        registerClass(components: components)
        dataSource.append(contentsOf: components)
        collectionView.clearReuseSubviews()
        collectionView.reloadData()
    }
    
    public func reloadData() {
        collectionView.clearReuseSubviews()
        collectionView.reloadData()
    }
    
    // 注册cell
    private func registerClass(components: [TSComponent]) {
        for cp in components {
            if let sectionCp = cp as? TSCollectionViewSectionComponent {
                if let header = sectionCp.headerComponent {
                    collectionView.register(header.viewClass, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: header.reuseIdentifier)
                }
                if let footer = sectionCp.footerComponent {
                    collectionView.register(footer.viewClass, forSupplementaryViewOfKind: UICollectionView.elementKindSectionFooter, withReuseIdentifier: footer.reuseIdentifier)
                }
                for cellCp in sectionCp.cells {
                    collectionView.register(cellCp.cellClass, forCellWithReuseIdentifier: cellCp.cellClass.description())
                }
            } else if let cellCp = cp as? TSCollectionViewCellComponent {
                collectionView.register(cellCp.cellClass, forCellWithReuseIdentifier: cellCp.cellClass.description())
            }
        }
    }
}

extension TSCollectionViewComponent {
    private var isSectionFramework: Bool {
        if dataSource.first is TSCollectionViewSectionComponent {
            return true
        }
        return false
    }
    
    public func cellComponent(at indexPath: IndexPath) -> TSCollectionViewCellComponent? {
        var cellCp: TSComponent?
        if isSectionFramework {
            if indexPath.section < dataSource.count,
                let sectionCp = dataSource[indexPath.section] as? TSCollectionViewSectionComponent,
                indexPath.item < sectionCp.cells.count {
                cellCp = sectionCp.cells[indexPath.item]
            }
        } else if indexPath.item < dataSource.count {
            cellCp = dataSource[indexPath.item]
        }
        if let cellCp = cellCp as? TSCollectionViewCellComponent {
            return cellCp
        }
        return nil
    }
}

extension TSCollectionViewComponent: UICollectionViewDataSource {
    
    public func numberOfSections(in collectionView: UICollectionView) -> Int {
        if isSectionFramework {
            return dataSource.count
        }
        return 1
    }
    
    public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        if isSectionFramework {
            if section < dataSource.count {
                return (dataSource[section] as? TSCollectionViewSectionComponent)?.cells.count ?? 0
            }
            return 0
        }
        return dataSource.count
    }
    
    public func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
        itemWillDisplayHandler?(cell, cellComponent(at: indexPath), indexPath)
    }
    
    public func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
        itemDidEndDisplayingHandler?(cell, cellComponent(at: indexPath), indexPath)
    }
    
    public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        guard let cellCp = cellComponent(at: indexPath) else {
            return collectionView.dequeueReusableCell(withReuseIdentifier: Self.defaultCellID, for: indexPath)
        }
        let cellClass = cellCp.cellClass
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellClass.description(), for: indexPath)
        if let cell = cell as? TSComponentView {
            var atts = attributes ?? [:]
            atts[kIndexPath] = indexPath
            cell.renderView(with: cellCp, component: self, attributes: atts)
        }
        return cell
    }
    
    public func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
        guard let sectionCp = dataSource[indexPath.section] as? TSCollectionViewSectionComponent else {
            return UICollectionReusableView()
        }
        if kind == UICollectionView.elementKindSectionHeader {
            if let headerCp = sectionCp.headerComponent {
                let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: headerCp.reuseIdentifier, for: indexPath)
                if let headerView = header as? TSComponentView {
                    var atts = attributes ?? [:]
                    atts[kIndexPath] = indexPath
                    headerView.renderView(with: headerCp, component: self, attributes: atts)
                }
                return header
            }
        } else if kind == UICollectionView.elementKindSectionFooter {
            if let footerCp = sectionCp.headerComponent {
                let footer = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: footerCp.reuseIdentifier, for: indexPath)
                if let footerView = footer as? TSComponentView {
                    var atts = attributes ?? [:]
                    atts[kIndexPath] = indexPath
                    footerView.renderView(with: footerCp, component: self, attributes: atts)
                }
                return footer
            }
        }
        return UICollectionReusableView()
    }
}

extension TSCollectionViewComponent: UICollectionViewDelegateFlowLayout {
    public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        guard let cellCp = cellComponent(at: indexPath) else {
            return .zero
        }
        return cellCp.cellSize(with: attributes)
    }
    
    public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
        if isSectionFramework {
            if section < dataSource.count {
                return (dataSource[section] as? TSCollectionViewSectionComponent)?.sectionInset ?? .zero
            }
        }
        return UIEdgeInsets(top: 10, left: 16, bottom: 10, right: 16)
    }
    
    public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
        if section < dataSource.count {
            return (dataSource[section] as? TSCollectionViewSectionComponent)?.lineSpacing ?? 12.0
        }
        return 12.0
    }
    
    public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
        if section < dataSource.count {
            return (dataSource[section] as? TSCollectionViewSectionComponent)?.itemSpacing ?? 12.0
        }
        return 12.0
    }
    
    public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize {
        if isSectionFramework {
            if section < dataSource.count,
                let sectionCp = dataSource[section] as? TSCollectionViewSectionComponent {
                return sectionCp.footerComponent?.viewSize ?? .zero
            }
        }
        return .zero
    }
    
    public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
        if isSectionFramework {
            if section < dataSource.count,
                let sectionCp = dataSource[section] as? TSCollectionViewSectionComponent {
                return sectionCp.headerComponent?.viewSize ?? .zero
            }
        }
        return .zero
    }
}

extension TSCollectionViewComponent: UICollectionViewDelegate {
    public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        itemDidSelectedHandler?(cellComponent(at: indexPath), indexPath)
    }
    
    public func scrollViewDidScroll(_ scrollView: UIScrollView) {
        didScrollHandler?(scrollView)
    }
    
    public func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
        willEndDraggingWithVelocityHandler?(scrollView, velocity)
    }
    
    public func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
        willBeginDraggingHandler?(scrollView)
    }
    
    public func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
        didEndDraggingHandler?(scrollView, decelerate)
    }
    
    public func scrollViewDidScrollToTop(_ scrollView: UIScrollView) {
        didScrollToTopHandler?(scrollView)
    }
    
    public func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
        didEndDeceleratingHandler?(scrollView)
    }
}
